package com.xcity.game.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.util.Arrays;
import java.util.Base64;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xcity.db.entity.Activity;
import com.xcity.db.entity.Mail;
import com.xcity.db.entity.Player;
import com.xcity.game.activity2.ActivityService2;
import com.xcity.game.activity2.platform.jg.JGGiftActivity;
import com.xcity.game.chat.ChatService;
import com.xcity.game.chat.ChatService.Forbidden;
import com.xcity.game.common.Cause;
import com.xcity.game.http.gm.GMService;
import com.xcity.game.http.impl.GMReward;
import com.xcity.game.player.PlayerService;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.pomelo.push.NoticePush;

import naga.x.App;
import naga.x.agent.Agent;
import naga.x.game.time.TimeUpdater;
import naga.x.util.CommonUtils;
import naga.x.util.IOUtils;
import naga.x.util.NumberUtils;
import naga.x.util.StringUtils;
import naga.x.util.TimeUtils;

public class GMServlet extends HttpServlet {

	protected static final Logger LOG = HttpService.LOG;
	public static final String CMD_REWARD = "reward";
	public static final String CMD_NOTICE = "notice";
	public static final String CMD_SEND_MAIL = "mail";
	public static final String CMD_PATCH = "patch";
	public static final String CMD_ADD_ACTIVITY = "add_act2";
	public static final String CMD_DELETE_ACTIVITY = "del_act2";
	public static final String CMD_UPDATE_ACTIVITY = "update_act2";
	public static final String CMD_QUERY_ACTIVITY = "find_act2";
	public static final String CMD_9G_SUBSCRIBE_CALLBACK = "9g_subscribe_cb"; // 9G用户关注公众号后的回调
	public static final String CMD_SYNC_SERVER_STATE = "sync_state"; // 同步服务器状态，由平台服调用
	public static final String CMD_HOTFIX = "hotfix";
	public static final String CMD_FORBID = "forbid";
	
	protected GMService service;
	
	public GMServlet(GMService service) {
		this.service = service;
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doWork(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doWork(req, resp);
	}
	
	void doWork(HttpServletRequest req, HttpServletResponse res) {
		try {
			String s = doWork0(req, res);
			response(res, s);
		} catch (Exception e) {
			e.printStackTrace();
			response(res, e.getMessage());
		}
	}
	
	String doWork0(HttpServletRequest req, HttpServletResponse res) {
		String cmd = req.getParameter("cmd");
		String data = req.getParameter("data");
		String sign = req.getParameter("sign");
		if (!makeSign(cmd, data).equals(sign)) {
			return "sign error";
		}
		if (CMD_REWARD.equals(cmd)) {
			return doReward(res, data);
		}
		if (CMD_NOTICE.equals(cmd)) {
			return doNotice(res, data);
		}
		if (CMD_SEND_MAIL.equals(cmd)) {
			return doMail(res, data);
		}
		if (CMD_PATCH.equals(cmd)) {
			return doPatch(res, data);
		}
		if (CMD_9G_SUBSCRIBE_CALLBACK.equals(cmd)) {
			return jgSubscribeCallback(res, data);
		}
		if (CMD_ADD_ACTIVITY.equals(cmd)) {
			return addActivity(res, data);
		}
		if (CMD_DELETE_ACTIVITY.equals(cmd)) {
			return removeActivity(res, data);
		}
		if (CMD_UPDATE_ACTIVITY.equals(cmd)) {
			return updateActivity(res, data);
		}
		if (CMD_QUERY_ACTIVITY.equals(cmd)) {
			return queryActivity(res, data);
		}
		if (CMD_HOTFIX.equals(cmd)) {
			return hotfix(res, data);
		}
		if (CMD_FORBID.equals(cmd)) {
			return forbid(res, data);
		}
		return "not support";
	}
	
	String doReward(HttpServletResponse res, String data) {
		try {
			byte[] b = Base64.getUrlDecoder().decode(StringUtils.stringToBytes(data, IOUtils.UTF8));
			// { uid: xx, msg: xx, gold: xx, stone: xx, items: [ { id: xx, count: xx } ] }
			GMReward reward = JSON.parseObject(b, GMReward.class);
			App.getApp().getServiceManager().get(HttpService.class).offerReward(reward);
			return "ok";
		} catch (Exception e) {
			LOG.error("[GM REWARD ERROR]", e);
			return "parse error";
		}
	}
	
	String doNotice(HttpServletResponse res, String data) {
		byte[] b = Base64.getUrlDecoder().decode(StringUtils.stringToBytes(data, IOUtils.UTF8));
		String msg = StringUtils.bytesToString(b, IOUtils.UTF8);
		App.getApp().getServiceManager().get(PlayerService.class).sendNotice(NoticePush.NOTICE_MARQUEE, msg);
		App.getApp().getServiceManager().get(ChatService.class).sendSystemChat(msg);
		return "ok";
	}
	
	String doMail(HttpServletResponse res, String data) {
		byte[] b = Base64.getUrlDecoder().decode(StringUtils.stringToBytes(data, IOUtils.UTF8));
		Mail mail = JSON.parseObject(b, Mail.class);
		service.sendMail(mail);
		return "ok";
	}
	
	String doPatch(HttpServletResponse res, String patchName) {
		try {
			Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(patchName);
			Runnable patch = (Runnable) clazz.newInstance();
			patch.run();
			return "ok";
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	String jgSubscribeCallback(HttpServletResponse res, String uid) {
//		1: 发放成功
//		0: 已发放过奖励忽略
//		-1: 发放失败
		ActivityService2 as2 = App.getApp().getServiceManager().get(ActivityService2.class);
		JGGiftActivity actImpl = as2.findImpl(JGGiftActivity.class);
		if (actImpl == null) {
			return "-1";
		}
		int code = actImpl.subscribeCallback(uid);
		return String.valueOf(code);
	}
	
	private String addActivity(HttpServletResponse res, String data) {
		byte[] b = Base64.getUrlDecoder().decode(StringUtils.stringToBytes(data, IOUtils.UTF8));
		Activity activity = JSON.parseObject(b, Activity.class);
		ActivityService2 service = App.getApp().getServiceManager().get(ActivityService2.class);
		Activity exist = service.find(activity.getUniqueId());
		if (exist != null) {
			return "activity existed";
		}
		try {
			Class.forName(activity.getImplClass());
		} catch (Exception e) {
			return "activity impl class not found:" + activity.getImplClass();
		}
		service.add(activity, Cause.GM);
		return "ok";
	}
	
	private String removeActivity(HttpServletResponse res, String data) {
		int id = NumberUtils.parseInt(data);
		ActivityService2 service = App.getApp().getServiceManager().get(ActivityService2.class);
		Activity act = service.remove(id, "GM");
		if (act != null) {
			return "ok";
		}
		return "activity not found";
	}
	
	private String updateActivity(HttpServletResponse res, String data) {
		byte[] b = Base64.getUrlDecoder().decode(StringUtils.stringToBytes(data, IOUtils.UTF8));
		Activity activity = JSON.parseObject(b, Activity.class);
		try {
			Class.forName(activity.getImplClass());
		} catch (Exception e) {
			return "activity impl class not found:" + activity.getImplClass();
		}
		ActivityService2 service = App.getApp().getServiceManager().get(ActivityService2.class);
		service.update(activity, Cause.GM);
		return "ok";
	}
	
	private String queryActivity(HttpServletResponse res, String data) {
		ActivityService2 service = App.getApp().getServiceManager().get(ActivityService2.class);
		if (StringUtils.isEmpty(data)) {
			return Arrays.toString(service.getActivities());
		}
		Activity activity = service.find(NumberUtils.parseInt(data));
		if (activity != null) {
			return activity.toString();
		}
		return "activity not found:" + data;
	}
	
	private String hotfix(HttpServletResponse res, String classname) {
		Instrumentation inst = Agent.inst;
		if (inst == null) {
			return "Instrumentation is null";
		}
		int n = classname.lastIndexOf('.');
		String simpleclassname;
		if (n != -1) {
			simpleclassname = classname.substring(n + 1);
		} else {
			simpleclassname = classname;
		}
		File file = new File("./patch/" + simpleclassname + ".class");
		if (!file.exists()) {
			return "the class file not found: " + file.getAbsolutePath();
		}
		try {
			ClassDefinition classDef = new ClassDefinition(Class.forName(classname), IOUtils.readBytes(new FileInputStream(file)));
			inst.redefineClasses(classDef);
			return "hotfix ok";
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return "hotfix fail: " + e.getMessage();
		}
	}
	
	private String forbid(HttpServletResponse res, String data) {
		byte[] b = Base64.getUrlDecoder().decode(StringUtils.stringToBytes(data, IOUtils.UTF8));
		// { action: add/remove/view, id: player's id, time: forbid time(minutes) }
		JSONObject json = JSON.parseObject(b, JSONObject.class);
		String action = json.getString("action");
		if ("add".equals(action)) {
			long id = json.getLongValue("id");
			Player player;
			if (id == 0L || (player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(id)) == null) {
				return "player not found:" + id;
			}
			int time = json.getIntValue("time");
			if (time <= 0) {
				return "forbid time error:" + time;
			}
			long endTime = TimeUpdater.getInstance().now() + time * 60000L;
			App.getApp().getServiceManager().get(ChatService.class).addForbidden(id, endTime);
			return "forbid succ: id=" + player.getId() + ", name=" + player.getName() + ", endtime=" + TimeUtils.format(endTime);
		}
		if ("remove".equals(action)) {
			long id = json.getLongValue("id");
			Forbidden forbidden = App.getApp().getServiceManager().get(ChatService.class).removeForbidden(id);
			if (forbidden == null) {
				return "the player not in forbidden";
			}
			Player player;
			if ((player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(id)) == null) {
				return "player not found:" + id;
			}
			return "forbidden removed: id=" + player.getId() + ", name=" + player.getName();
		}
		if ("view".equals(action)) {
			Forbidden[] forbiddens = App.getApp().getServiceManager().get(ChatService.class).getForbiddens();
			if (forbiddens == null || forbiddens.length == 0) {
				return "forbidden empty";
			}
			PlayerCacheService pcs = App.getApp().getServiceManager().get(PlayerCacheService.class);
			StringBuilder builder = new StringBuilder(256);
			builder.append("forbidden list:\n");
			for (Forbidden fb : forbiddens) {
				Player player = pcs.find(fb.id);
				builder.append("id=").append(player.getId())
						.append(",name=").append(player.getName())
						.append(",endtime=").append(TimeUtils.format(fb.endTime))
						.append('\n');
			}
			return builder.toString();
		}
		return "cmd error";
	}
	
	static void response(HttpServletResponse res, String text) {
		res.setCharacterEncoding(IOUtils.UTF8);
		res.setStatus(HttpServletResponse.SC_OK);
		OutputStream out = null;
		try {
			out = res.getOutputStream();
			out.write(StringUtils.stringToBytes(text, IOUtils.UTF8));
			IOUtils.flushQuietly(out);
		} catch (Exception e) {
			LOG.error("[GM RESPONSE ERROR]", e);
		} finally {
			IOUtils.closeQuietly(out);
		}
	}
	
	static String makeSign(String cmd, String data) {
//		String s = "cmd=" + cmd + "&data=" + data + "&key=" + App.getApp().getConfig().getString(Config.SECRET_KEY);
		String s = "cmd=" + cmd + "&data=" + data + "&key=" + App.getApp().getConfig().secretKey;
		return CommonUtils.md5(s);
	}
	
}
